import EventEmitter from "events"
import { AsyncSeriesHook } from 'tapable';
import hotkeys from 'hotkeys-js';
import ServePlugin from '../plugins/ServePlugin'

class CoverEditor extends EventEmitter {
    constructor() {
        super()
        this.canvas = null
        this.contextMenu = null
        this.pluginMap = {

        }
        this.customEvents = []
        this.customApis = []
        this.hooks = [
            'hookImportBefore',
            'hookImportAfter',
            'hookSaveBefore',
            'hookSaveAfter',
        ]
        this.hooksEntity = {}
        this.boundHotKeys = new Set()
    }
    // 此方法覆盖父类EventEmitter的init方法
    init(canvas) {
        this.canvas = canvas
        this._initActionHooks();
        this._initServePlugin()
    }

    get fabricCanvas() {
        return this.canvas;
    }

    // 绑定插件
    use(pluginClass, opt = {}) {
        this._saveCustomAttr(pluginClass)
        // 初始化插件
        const pluginRunTime = new pluginClass(this.canvas, this, opt)
        // 添加插件名称
        pluginRunTime.pluginName = pluginRunTime.constructor.name
        // 装载插件到集合
        this.pluginMap[pluginRunTime.pluginName] = pluginRunTime

        this._bindingHooks(pluginRunTime)
        this._bindingHotkeys(pluginRunTime)
        this._bindingApis(pluginRunTime)
    }

    _initServePlugin() {
        this.use(ServePlugin)
    }

    // 生命周期事件
    _initActionHooks() {
        this.hooks.forEach((hookName) => {
            this.hooksEntity[hookName] = new AsyncSeriesHook(['data']);
        });
    }

    // 保存组件自定义事件与API
    _saveCustomAttr(plugin) {
        // events和apis 属于插件的static静态属性，直接访问
        const { events = [], apis = [] } = plugin;
        this.customApis = this.customApis.concat(apis);
        this.customEvents = this.customEvents.concat(events);
    }

    // 绑定事件hooks钩子的异步监听器tapPromise
    // 调用钩子：调用钩子名称+参数，eg:hookImportAfter(data)
    _bindingHooks(plugin) {
        this.hooks.forEach(hookName => {
            const hook = plugin[hookName]
            if (hook) {
                // tapPromise 方法来自_initActionHooks初始化AsyncSeriesHook
                this.hooksEntity[hookName].tapPromise(plugin.pluginName + hookName, function () {
                    const result = hook.apply(plugin, [...arguments])
                    // hook 兼容非 Promise 返回值
                    return result instanceof Promise ? result : Promise.resolve(result);
                })
            }
        })
    }

    // 绑定快捷键
    _bindingHotkeys(plugin) {
        plugin?.hotkeys?.forEach((keyName) => {
            // 先判断是否存在，不存在就绑定，防止事件污染
            if (!this.boundHotKeys.has(keyName)) {
                // 支持 keyup 为每个热键绑定一个事件处理函数
                hotkeys(keyName, { keyup: true }, (e, handle) => {
                    e.preventDefault();
                    e.stopPropagation();
                    // 如果插件对象有 hotkeyEvent 方法，就调用它，并传入当前的热键名称和事件对象
                    plugin.hotkeyEvent && plugin.hotkeyEvent(keyName, e);
                })
                this.boundHotKeys.add(keyName)
            }
        })
    }

    // 绑定插件api方法
    _bindingApis(plugin) {
        // apis 属于插件的static静态属性，直接访问
        const { apis = [] } = plugin.constructor || {}
        apis.forEach(apiName => {
            // 给当前 CoverEditor 绑上插件plugin的api
            this[apiName] = function () {
                if (!plugin[apiName]) {
                    throw new Error(`This ${apiName} method is not implemented inside the plugin`)
                }
                return plugin[apiName].apply(plugin, [...arguments]);
            };
        });
    }

    destory() {
        this.canvas = null;
        this.contextMenu = null;
        this.pluginMap = {};
        this.customEvents = [];
        this.customApis = [];
        this.hooksEntity = {};
    }

    // 解决 listener 为 undefined 的时候卸载错误
    off(eventName, listener) {
        // noinspection TypeScriptValidateTypes
        return listener ? super.off(eventName, listener) : this;
    }
}

export default CoverEditor